#include <gtest/gtest.h>
#include <vendor_global.h>
#include "xcom_process.h"
#include "xcom_handler.h"
#include "cmdvalue.h"

#define TS(func)                                                               \
	do {                                                                       \
		struct timeval t1, t2;                                                 \
		gettimeofday(&t1, NULL);                                               \
		func;                                                                  \
		gettimeofday(&t2, NULL);                                               \
		printf(#func ": %.3fmsecs\n", (t2.tv_sec - t1.tv_sec) * 1000.0 +       \
										  (t2.tv_usec - t1.tv_usec) / 1000.0); \
	} while (0)
namespace protocol {
class XComProcessTest : public ::testing::Test {
public:
	void SetUp() override
	{
		load = xcomParser_.LoadXComFile(
			VENDOR_TEST_PATH + "/test_xcom_parser.xcom");
	}

	void TearDown() override
	{
		xcomParser_.Clear();
		load = false;
	}

	XComParser xcomParser_;
	bool load = false;
};

TEST_F(XComProcessTest, Process)
{
	std::shared_ptr<XComNode> xcomNode =
		xcomParser_.GetXComNode("P056934A925C21000T");
	XComProcess xcomProcess(xcomNode, "00000024240033");
	bool result;
	TS(result = xcomProcess.Process(
		   "12L",
		   [&](const libxcom::CmdValue &cmd,
			   const ParseRecv &parseRecv) -> bool {
			   EXPECT_EQ(cmd.Data(), std::vector<uint8_t>({0x68, 0x10, 0x33,
										 0x00, 0x24, 0x24, 0x00, 0x00, 0x00, 01,
										 03, 0x1F, 0x90, 0x01, 0xA7, 0x16}));
			   std::vector<uint8_t> data = {0x68, 0x10, 0x33, 0x00, 0x24, 0x24,
				   0x00, 0x00, 0x00, 0x81, 20, 0x1F, 0x90, 0x01, 00, 00, 00, 00,
				   00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xA7,
				   0x16};
			   int result = parseRecv(data.data(), data.size());
			   EXPECT_EQ(result, RECV_COMPLETE);
			   return true;
		   },
		   {}, 1.00));
	xcomProcess.GetResult("dl").Show();
	bool flag = false;
	EXPECT_TRUE(xcomProcess.Process(
		"12P",
		[&](const libxcom::CmdValue &cmd, const ParseRecv &parseRecv) -> bool {
			flag = true;
			return true;
		},
		{}, 1.00));
	EXPECT_EQ(flag, false);
	EXPECT_FLOAT_EQ(xcomProcess.GetResult("12L").As<float>(), 0);
	EXPECT_FLOAT_EQ(xcomProcess.GetResult("12P").As<float>(), 0);

	EXPECT_EQ(xcomProcess.GetResult("data").As<libxcom::CmdValue>().Data(),
		std::vector<uint8_t>({00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
			00, 00, 00, 00, 00}));
}

TEST_F(XComProcessTest, Process2)
{
	std::shared_ptr<XComNode> xcomNode =
		xcomParser_.GetXComNode("P0046F56202C21000T");
	XComProcess xcomProcess(xcomNode, "01");
	bool result;
	TS(result = xcomProcess.Process(
		   "11X",
		   [&](const libxcom::CmdValue &cmd,
			   const ParseRecv &parseRecv) -> bool {
			   EXPECT_EQ(cmd.Data(), std::vector<uint8_t>({0x01, 0x03, 0x00,
										 0x00, 0x00, 0x02, 0x0B, 0xC4}));
			   std::vector<uint8_t> data = {
				   0x01, 0x03, 0x04, 0x42, 0x07, 0x00, 0x00, 0x37, 0x25};
			   int result = parseRecv(data.data(), data.size());
			   EXPECT_EQ(result, RECV_COMPLETE);
			   return true;
		   },
		   {}, 1.00));
	xcomProcess.GetResult("dl").Show();
	EXPECT_EQ(xcomProcess.GetResult("dl").As<int32_t>(), 4);
	EXPECT_FLOAT_EQ(xcomProcess.GetResult("11X").As<float>(), 33.75);
	EXPECT_EQ(xcomProcess.GetResult("data").As<libxcom::CmdValue>().Data(),
		std::vector<uint8_t>({0x42, 0x07, 0x00, 0x00}));
}

TEST_F(XComProcessTest, ProcessNotComplete)
{
	std::shared_ptr<XComNode> xcomNode =
		xcomParser_.GetXComNode("P056934A925C21000T");
	XComProcess xcomProcess(xcomNode, "00000024240033");

	auto func = [&](const libxcom::CmdValue &cmd,
					const ParseRecv &parseRecv) -> bool {
		EXPECT_EQ(cmd.Data(),
			std::vector<uint8_t>({0x68, 0x10, 0x33, 0x00, 0x24, 0x24, 0x00,
				0x00, 0x00, 01, 03, 0x1F, 0x90, 0x01, 0xA7, 0x16}));
		std::vector<uint8_t> data1 = {0x68, 0x10, 0x33, 0x00, 0x24};
		std::vector<uint8_t> data2 = {
			0x24, 0x00, 0x00, 0x00, 0x81, 20, 0x1F, 0x90, 0x01, 00, 00, 00};
		std::vector<uint8_t> data3 = {
			00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xA7, 0x16};
		for (int32_t i = 0; i < 3; ++i) {
			if (i == 0) {
				int result = parseRecv(data1.data(), data1.size());
				EXPECT_EQ(result, RECV_NOT_COMPLETE);
			} else if (i == 1) {
				int result = parseRecv(data2.data(), data2.size());
				EXPECT_EQ(result, RECV_NOT_COMPLETE);
			} else {
				int result = parseRecv(data3.data(), data3.size());
				EXPECT_EQ(result, RECV_COMPLETE);
			}
		}
		return true;
	};
	EXPECT_TRUE(xcomProcess.Process("12L", func, {}, 1.00));
}

TEST_F(XComProcessTest, ProcessFail)
{
	std::shared_ptr<XComNode> xcomNode =
		xcomParser_.GetXComNode("P056934A925C21000T");
	XComProcess xcomProcess(xcomNode, "00000024240033");
	EXPECT_FALSE(xcomProcess.Process(
		"12L",
		[&](const libxcom::CmdValue &cmd, const ParseRecv &parseRecv) -> bool {
			EXPECT_EQ(cmd.Data(),
				std::vector<uint8_t>({0x68, 0x10, 0x33, 0x00, 0x24, 0x24, 0x00,
					0x00, 0x00, 01, 03, 0x1F, 0x90, 0x01, 0xA7, 0x16}));
			std::vector<uint8_t> data = {0x68, 0x10, 0x33, 0x00, 0x24, 0x24,
				0x00, 0x00, 0x00, 0x81, 20, 0x1F, 0x91, 0x01, 00, 00, 00, 00,
				00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xA7, 0x16};
			int result = parseRecv(data.data(), data.size());
			EXPECT_EQ(result, RECV_FAILED);
			return false;
		},
		{}, 1.00));
}
} // namespace protocol

int main(int argc, char *argv[])
{
	utils::set_enable(true);
	::testing::InitGoogleTest(&argc, argv);
	return RUN_ALL_TESTS();
}